import moment, { Moment } from "moment";

import { config } from "@src/config";

export const MIN_WEEKDAY = 1;
export const MAX_WEEKDAY = 7;
export const MIN_HOUR = 0;
export const MAX_HOUR = 23;

export const HOURS_PER_DAY = 24;
export const SECONDS_PER_HOUR = 3600;

const DAYS_PER_WEEK = 7;
const ONE_WEEK = 7;
const TWO_WEEKS = 14;

const YEAR_LENGTH = 4;
const MONTH_LENGTH = 2;
const DAY_LENGTH = 2;

export const getNextWeekDayTime = (weekDay: number, hour: number) => {
  return getUtcMoment()
    .utcOffset(config.timezoneOffset)
    .day(weekDay + DAYS_PER_WEEK)
    .hours(hour)
    .minutes(0)
    .seconds(0)
    .milliseconds(0)
    .valueOf();
};

export const getNow = () => {
  return getUtc();
};

// startHourTimestamp, endHourTimestamp, startDay
export const getScienerMeta = (
  start: Date,
  end: Date
): [number, number, number] => {
  const ms = getUtcMoment(start).utcOffset(config.timezoneOffset);
  const me = getUtcMoment(end).utcOffset(config.timezoneOffset);

  return [
    ms
      .hours(ms.hours() >= 1 ? ms.hours() - 1 : 0)
      .minutes(0)
      .seconds(0)
      .milliseconds(0)
      .valueOf(),
    me
      .hours(
        me.minutes() === 0 || me.hours() <= MAX_HOUR
          ? me.hours()
          : me.hours() + 1
      )
      .minutes(0)
      .seconds(0)
      .milliseconds(0)
      .valueOf(),
    me.date()
  ];
};

export const getSubscribeMessageDate = (date: Date): string => {
  return getUtcMoment(date)
    .utcOffset(config.timezoneOffset)
    .format("YYYY-MM-DD HH:mm:ss");
};

export const getToday = () => {
  return getUtcMoment()
    .utcOffset(config.timezoneOffset)
    .hours(0)
    .minutes(0)
    .seconds(0)
    .milliseconds(0)
    .toDate();
};

export const getTodayString = () => {
  return getUtcMoment().format("YYYYMMDD");
};

export const getTomorrow = () => {
  const tomorrow = getUtcMoment()
    .utcOffset(config.timezoneOffset)
    .hours(0)
    .minutes(0)
    .seconds(0)
    .milliseconds(0)
    .add(1, "day");

  const start = tomorrow.valueOf();
  const end = tomorrow.add(1, "day").valueOf(); // NOTE: this affects `tomorrow`

  return {
    end: end,
    start: start
  };
};

export const getUtc = (
  date: string | number | void | moment.Moment | Date
): Date => {
  return getUtcMoment(date).toDate();
};

export const getUtcMoment = (
  date: string | number | void | moment.Moment | Date
): Moment => {
  return moment.utc(date);
};

export const getZoneTime = (
  date: string | number | void | moment.Moment | Date
): Date => {
  return getZoneMoment(date).toDate();
};

export const getZoneMoment = (
  date: string | number | void | moment.Moment | Date
): Moment => {
  return getUtcMoment(date).utcOffset(config.timezoneOffset);
};

export const getZoneTimeFromOa = (
  date: string,
  hour: string,
  minute: string
): Date => {
  return getZoneMoment()
    .year(parseInt(date.substr(0, YEAR_LENGTH), 10))
    .month(parseInt(date.substr(YEAR_LENGTH, YEAR_LENGTH + MONTH_LENGTH), 10))
    .days(
      parseInt(
        date.substr(
          YEAR_LENGTH + MONTH_LENGTH,
          YEAR_LENGTH + MONTH_LENGTH + DAY_LENGTH
        ),
        10
      )
    )
    .hours(parseInt(hour, 10))
    .minutes(parseInt(minute, 10))
    .seconds(0)
    .milliseconds(0)
    .utc()
    .toDate();
};

export const isToday = (date: Date): boolean => {
  const m = getUtcMoment(date);
  const now = getUtcMoment();

  return (
    m.year() === now.year() &&
    m.month() === now.month() &&
    m.date() === now.date()
  );
};

export const oneWeekAfter = (date: Date): Date => {
  const m = getUtcMoment(date);

  return m.date(m.date() + ONE_WEEK).toDate();
};

export const twoWeeksAfter = (date: Date): Date => {
  const m = getUtcMoment(date);

  return m.date(m.date() + TWO_WEEKS).toDate();
};

export const oneYearAfterString = (): string => {
  const m = getUtcMoment();

  return m.date(m.year() + 1).format("YYYYMMDD");
};

export const weeksAfter = (date: Date, weeks: number, seconds = 0): Date => {
  const m = getUtcMoment(date);

  return m
    .date(m.date() + weeks * DAYS_PER_WEEK)
    .seconds(seconds)
    .toDate();
};
